package cn.tworice.cryption.aop;

import cn.tworice.common.vo.RequestResult;
import cn.tworice.common.annotation.NoEncryption;
import cn.tworice.cryption.config.CryptionProperties;
import cn.tworice.cryption.constand.CryptConst;
import cn.tworice.cryption.service.CryptService;
import cn.tworice.cryption.util.RSAUtil;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import java.lang.reflect.Method;

@Slf4j
@Aspect
@Component
public class CryptionAspect {

    @Resource
    private CryptionProperties cryptionProperties;

    @Resource
    private CryptService cryptService;

    /**
     * 加解密
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around(value = "@within(io.swagger.annotations.Api)")
    public Object after(ProceedingJoinPoint pjp) throws Throwable {
        if(!cryptionProperties.getEnable() || !isPointcutMatch(pjp, cryptionProperties.getPointcut())){
            return pjp.proceed();
        }
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        // 继续执行方法，并获取执行结果
        Object proceed = pjp.proceed(pjp.getArgs());
        // 返回结果之前加签名
        if (this.hasEncrypt(((MethodSignature) pjp.getSignature()).getMethod())
                && (proceed instanceof RequestResult)) {
            proceed = this.requestResult(proceed);
        }
        return proceed;
    }

    private Object requestResult(Object proceed) throws Exception {
        RequestResult result = (RequestResult) proceed;
        RequestResult cryptionResult = RequestResult.success(CryptConst.CODE)
                .appendData(CryptConst.RESULT, cryptService.encrypt(JSON.toJSONString(result.getData())))
                .appendData("type", cryptionProperties.getType());
        if ("RSA+AES-GCM".equals(cryptionProperties.getType())) {
            cryptionResult.appendData("aes", cryptionProperties.getKey())
                    .appendData("privateKey", RSAUtil.getPrivateKeyAsBase64(cryptionProperties.getPrivateKey()));
        }
        return cryptionResult;
    }

    /**
     * 判断切入点与表达式是否符合
     * @param joinPoint 切入点
     * @param expression 表达式
     * @return 是否符合
     */
    private boolean isPointcutMatch(ProceedingJoinPoint joinPoint, String expression) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(expression);
        return pointcut.matches(joinPoint.getSignature().getDeclaringType());
    }

    /**
     * 判断当前方法是否需要加密操作
     * @param method 方法
     * @return 是否需要加密
     */
    private boolean hasEncrypt(Method method) {
        return !method.isAnnotationPresent(NoEncryption.class);
//        if (method.isAnnotationPresent(NoEncryption.class) && !method.isAnnotationPresent(ApiOperation.class)) {
//            return false;
//        }
//
//        return true;
    }
}